package com.joker.demo.admin.service.smart;


import cn.hutool.core.collection.CollUtil;
import com.joker.demo.admin.controller.JobTriggerController;
import com.joker.demo.admin.domain.constants.JobInfoConstants;
import com.joker.demo.admin.domain.dto.JobInfoQueryDTO;
import com.joker.demo.admin.domain.po.JobInfoPO;
import com.joker.demo.admin.service.JobInfoService;
import com.joker.demo.core.domain.enums.JobStatusEnum;
import com.joker.demo.core.scheduler.JobScheduler;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.time.DateUtils;
import org.apache.commons.lang3.time.StopWatch;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * 定时任务触发逻辑处理器
 * 默认关闭状态，支持手动开启/关闭，开启后持续执行任务
 *
 * @author : feixiang.li
 * @since : 2025-07-25 17:47
 */
@Slf4j
@Component
public class JobTriggerHandleHelper {

    @Resource
    private JobInfoService jobInfoService;

    @Resource
    private JobScheduler jobScheduler;

    /**
     * 任务执行状态控制
     */
    private final AtomicBoolean isRunning = new AtomicBoolean(false);

    /**
     * 启动时间
     */
    private volatile String startTime;

    /**
     * 最后更新时间
     */
    private volatile String lastUpdateTime;

    /**
     * 定时任务执行器
     */
    private ScheduledExecutorService schedulerExecutor;

    /**
     * 任务执行线程
     */
    private volatile Thread taskThread;

    /**
     * 日期格式化器
     */
    private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    @PostConstruct
    public void init() {
        log.info("定时任务触发处理器初始化完成，默认状态：关闭");
        
        // 创建定时任务执行器
        schedulerExecutor = Executors.newScheduledThreadPool(1, r -> {
            Thread thread = new Thread(r, "JobTriggerHandler-Thread");
            thread.setDaemon(true);
            return thread;
        });
    }

    /**
     * 手动启动定时任务触发处理器
     */
    public synchronized void startJobTrigger() {
        if (isRunning.get()) {
            log.warn("定时任务触发处理器已经在运行中");
            return;
        }

        log.info("启动定时任务触发处理器");
        isRunning.set(true);
        startTime = DATE_FORMAT.format(new Date());
        lastUpdateTime = startTime;

        // 启动任务执行线程
        taskThread = new Thread(() -> {
            log.info("定时任务执行线程启动");
            while (isRunning.get()) {
                try {
                    log.debug("开始执行定时任务触发逻辑");
                    lastUpdateTime = DATE_FORMAT.format(new Date());
                    // 执行任务触发逻辑
                    triggerHandle();
                }  catch (Exception e) {
                    log.error("定时任务触发处理异常", e);
                    // 发生异常时短暂休眠，避免无限循环
                    try {
                        if (isRunning.get()) {
                            Thread.sleep(5000);
                        }
                    } catch (InterruptedException ie) {
                        Thread.currentThread().interrupt();
                        break;
                    }
                }
            }
            log.info("定时任务执行线程结束");
        }, "JobTrigger-TaskThread");
        
        taskThread.setDaemon(true);
        taskThread.start();
        
        log.info("定时任务触发处理器启动完成");
    }

    /**
     * 手动停止定时任务触发处理器
     */
    public synchronized void stopJobTrigger() {
        if (!isRunning.get()) {
            log.warn("定时任务触发处理器已经停止");
            return;
        }

        log.info("停止定时任务触发处理器");
        isRunning.set(false);
        lastUpdateTime = DATE_FORMAT.format(new Date());

        // 中断任务执行线程
        if (taskThread != null && taskThread.isAlive()) {
            taskThread.interrupt();
            try {
                // 等待线程结束，最多等待5秒
                taskThread.join(5000);
                if (taskThread.isAlive()) {
                    log.warn("任务执行线程未能在5秒内正常结束");
                }
            } catch (InterruptedException e) {
                log.warn("等待任务执行线程结束时被中断");
                Thread.currentThread().interrupt();
            }
        }
        
        log.info("定时任务触发处理器已停止");
    }

    /**
     * 获取定时任务触发处理器状态
     */
    public JobTriggerController.JobTriggerStatusVO getJobTriggerStatus() {
        String statusDescription = isRunning.get() ? "正在运行" : "已停止";
        return new JobTriggerController.JobTriggerStatusVO(
                isRunning.get(),
                statusDescription,
                startTime != null ? startTime : "未启动",
                lastUpdateTime != null ? lastUpdateTime : "未启动"
        );
    }

    /**
     * 应用关闭时优雅地关闭线程池
     */
    @PreDestroy
    public void destroy() {
        log.info("开始关闭定时任务触发处理器");
        
        // 停止任务执行
        stopJobTrigger();
        
        // 关闭线程池
        if (schedulerExecutor != null && !schedulerExecutor.isShutdown()) {
            schedulerExecutor.shutdown();
            try {
                if (!schedulerExecutor.awaitTermination(10, TimeUnit.SECONDS)) {
                    schedulerExecutor.shutdownNow();
                }
            } catch (InterruptedException e) {
                schedulerExecutor.shutdownNow();
                Thread.currentThread().interrupt();
            }
        }
        log.info("定时任务触发处理器已完全关闭");
    }

    @Transactional
    public void triggerHandle() {
        // 检查任务执行器是否初始化
        boolean initialized = jobScheduler.isInitialized();
        if (!initialized) {
            log.info("任务执行器未初始化完成，跳过本次执行");
            return;
        }

        log.info("开始执行定时任务触发逻辑");
        
        // 构建查询条件
        JobInfoQueryDTO jobInfoQueryDTO = buildJobInfoQueryDTO();
        
        // 记录执行时间统计
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();

        long totalQueryTime = 0;
        long totalProcessTime = 0;
        int totalProcessedTasks = 0;

        try {
            while (isRunning.get()) { // 检查是否应该继续执行
                log.debug("查询待处理任务，查询条件: {}", jobInfoQueryDTO);

                // 记录查询任务时间
                StopWatch queryWatch = new StopWatch();
                queryWatch.start();
                List<JobInfoPO> jobInfoList = jobInfoService.queryJobInfo(jobInfoQueryDTO);
                queryWatch.stop();
                totalQueryTime += queryWatch.getTime();

                if (CollUtil.isEmpty(jobInfoList)) {
                    log.info("没有查询到待处理任务，退出循环");
                    break;
                }
                
                log.info("查询到待处理任务: {} 个", jobInfoList.size());
                jobInfoQueryDTO.setLastTableSourceId(getMaxId(jobInfoList));

                // 记录处理任务时间
                StopWatch processWatch = new StopWatch();
                processWatch.start();
                
                // 处理任务-生成日志明细、发送任务执行请求
                jobInfoService.handleMultipleJobInfo(jobInfoList, false);
                
                processWatch.stop();
                totalProcessTime += processWatch.getTime();

                totalProcessedTasks += jobInfoList.size();
                log.info("本次处理任务数量: {}, 查询耗时: {}ms, 处理耗时: {}ms", 
                        jobInfoList.size(), queryWatch.getTime(), processWatch.getTime());
            }
        } finally {
            stopWatch.stop();
            long totalTime = stopWatch.getTime();
            log.info("定时任务触发处理完成，执行时间统计:");
            log.info("总执行时间: {}ms", totalTime);
            log.info("查询任务总耗时: {}ms", totalQueryTime);
            log.info("处理任务总耗时: {}ms", totalProcessTime);
            log.info("总处理任务数量: {}", totalProcessedTasks);
            if (totalProcessedTasks > 0) {
                log.info("平均每个任务处理时间: {}ms", totalTime / totalProcessedTasks);
            }
        }
    }

    private JobInfoQueryDTO buildJobInfoQueryDTO() {
        Date now = new Date();
        Date nextTriggerTime = DateUtils.addSeconds(now, JobInfoConstants.triggerInterval);
        JobInfoQueryDTO jobInfoQueryDTO = new JobInfoQueryDTO();
        jobInfoQueryDTO.setTriggerStatus(JobStatusEnum.READY.getCode());
        jobInfoQueryDTO.setStartTime(nextTriggerTime);
        jobInfoQueryDTO.setPageSize(JobInfoConstants.maxTriggerCount);
        jobInfoQueryDTO.setLastTableSourceId(-1L);
        return jobInfoQueryDTO;
    }

    private Long getMaxId(List<JobInfoPO> jobInfoList) {
        if (CollUtil.isEmpty(jobInfoList)) {
            return null;
        }
        return jobInfoList.stream()
                .filter(Objects::nonNull)
                .map(JobInfoPO::getId)
                .max(Long::compare)
                .orElse(null);
    }
}
